use std::sync::Arc;

use axum::{extract::State, routing::post, Json, Router};
use chrono::Local;
use serde::Serialize;
use validator::Validate;

use crate::{
    form,
    handler::{get_conn, log_error, Response},
    hcaptcha,
    jwt::{self, AdminClaimsData, UserClaimsData},
    model, service, utils, AppState, Error, Result,
};

#[derive(Serialize)]
pub struct LoginResponse {
    #[serde(flatten)]
    pub auth: jwt::AuthData,

    pub user: model::user::User,
}

pub async fn user_login(
    State(state): State<Arc<AppState>>,
    Json(frm): Json<form::auth::Login>,
) -> Result<Json<Response<LoginResponse>>> {
    let handler_name = "auth_api/LoginResponse";

    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    if !hcaptcha::HCaptcha::from_cfg(&state.cfg.hcaptcha)
        .verify(&frm.captcha)
        .await
        .map_err(log_error(handler_name))?
    {
        return Err(Error::invalid_parameter("人机验证失败"));
    }

    let conn = get_conn(&state);

    let user = service::user::user_login(&conn, &frm.email, &frm.password)
        .await
        .map_err(log_error(handler_name))?;

    let auth = jwt::token::encode(&state.cfg.user_jwt, UserClaimsData::from(user.clone()))
        .map_err(log_error(handler_name))?;

    Ok(Response::ok(LoginResponse { auth, user }).to_json())
}

pub async fn admin_login(
    State(state): State<Arc<AppState>>,
    Json(frm): Json<form::auth::Login>,
) -> Result<Json<Response<LoginResponse>>> {
    let handler_name = "auth_api/admin_login";

    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    if !hcaptcha::HCaptcha::from_cfg(&state.cfg.hcaptcha)
        .verify(&frm.captcha)
        .await
        .map_err(log_error(handler_name))?
    {
        return Err(Error::invalid_parameter("人机验证失败"));
    }

    let conn = get_conn(&state);

    let user = service::user::admin_login(&conn, &frm.email, &frm.password)
        .await
        .map_err(log_error(handler_name))?;

    let auth = jwt::token::encode(&state.cfg.admin_jwt, AdminClaimsData::from(user.clone()))
        .map_err(log_error(handler_name))?;

    Ok(Response::ok(LoginResponse { auth, user }).to_json())
}

pub async fn user_register(
    State(state): State<Arc<AppState>>,
    Json(frm): Json<form::auth::Register>,
) -> Result<Json<Response<LoginResponse>>> {
    let handler_name = "auth/user_register";
    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    if !hcaptcha::HCaptcha::from_cfg(&state.cfg.hcaptcha)
        .verify(&frm.captcha)
        .await
        .map_err(log_error(handler_name))?
    {
        return Err(Error::invalid_parameter("人机验证失败"));
    }

    let pool = get_conn(&state);

    let password = utils::password::hash(&frm.password).map_err(log_error(handler_name))?;

    let m = model::user::User {
        email: frm.email.clone(),
        password,
        status: model::user::Status::Actived,
        role: model::user::Role::Member,
        dateline: Local::now(),
        ..Default::default()
    };

    service::user::add(&pool, &m)
        .await
        .map_err(log_error(handler_name))?;

    let user = service::user::user_login(&pool, &frm.email, &frm.password)
        .await
        .map_err(log_error(handler_name))?;
    let auth = jwt::token::encode(&state.cfg.user_jwt, UserClaimsData::from(user.clone()))
        .map_err(log_error(handler_name))?;

    Ok(Response::ok(LoginResponse { auth, user }).to_json())
}

pub fn route(state: Arc<AppState>) -> Router {
    Router::new()
        .route("/user-login", post(user_login))
        .route("/user-register", post(user_register))
        .route("/admin-login", post(admin_login))
        .with_state(state)
}
